import tabulate

from coin.exchange.base.kr_rest.futures_product import NativeFuturesProduct
from coin.exchange.base.kr_rest.product import NativeProduct
from coin.exchange.base.order_gateway import OrderGatewayBase


class WorkingOrderState(object):
  def __init__(self, product):
    assert isinstance(product, (NativeProduct, NativeFuturesProduct))
    self._product = product
    self._reset_order_state()

  @property
  def product(self):
    return self._product

  @property
  def sell_on_the_fly_orders(self):
    return self._sell_on_the_fly_orders

  @property
  def sell_working_orders(self):
    return self._sell_working_orders

  @property
  def sell_cancel_on_the_fly_orders(self):
    return self._sell_cancel_on_the_fly_orders

  @property
  def buy_on_the_fly_orders(self):
    return self._buy_on_the_fly_orders

  @property
  def buy_working_orders(self):
    return self._buy_working_orders

  @property
  def buy_cancel_on_the_fly_orders(self):
    return self._buy_cancel_on_the_fly_orders

  @property
  def get_sell_unack_count(self):
    return len(self._sell_on_the_fly_orders)

  @property
  def get_sell_unack_qty(self):
    return abs(self._sell_on_the_fly_potential)

  @property
  def get_buy_unack_count(self):
    return len(self._buy_on_the_fly_orders)

  @property
  def get_buy_unack_qty(self):
    return abs(self._buy_on_the_fly_potential)

  @property
  def get_sell_cancel_unack_count(self):
    return len(self._sell_cancel_on_the_fly_orders)

  @property
  def get_buy_cancel_unack_count(self):
    return len(self._buy_cancel_on_the_fly_orders)

  def _reset_order_state(self):
    self._sell_on_the_fly_orders = []
    self._sell_working_orders = []
    self._sell_cancel_on_the_fly_orders = []
    self._sell_orders = []
    self._sell_on_the_fly_potential = 0
    self._sell_working_potential = 0
    self._sell_cancel_on_the_fly_potential = 0
    self._sell_potential = 0
    self._buy_on_the_fly_orders = []
    self._buy_working_orders = []
    self._buy_cancel_on_the_fly_orders = []
    self._buy_orders = []
    self._buy_on_the_fly_potential = 0
    self._buy_working_potential = 0
    self._buy_cancel_on_the_fly_potential = 0
    self._buy_potential = 0

  def print_state_table(self, logger, buy_manage_ret="", sell_manage_ret=""):
    table = []
    headers = [
        'S',  # side
        'T',  # total
        'SP',  # submit pending
        'A',  # active
        'CP',  # cancel pending
        'PP',  # position potential
        'MR',  # manage result
    ]
    table.append([
        'S',
        len(self._sell_orders),
        len(self._sell_on_the_fly_orders),
        len(self._sell_working_orders),
        len(self._sell_cancel_on_the_fly_orders),
        self._sell_potential,
        sell_manage_ret,
    ])
    table.append([
        'B',
        len(self._buy_orders),
        len(self._buy_on_the_fly_orders),
        len(self._buy_working_orders),
        len(self._buy_cancel_on_the_fly_orders),
        self._buy_potential,
        buy_manage_ret,
    ])
    print(self._product)
    print(tabulate.tabulate(table, headers=headers))

  def print_state(self, logger):
    logger.info("%s %s %s %s %s %s %s %s %s %s %s %s" % (
        self._product,
        "State:",
        self._sell_potential,
        self._buy_potential,
        len(self._sell_orders),
        len(self._sell_on_the_fly_orders),
        len(self._sell_working_orders),
        len(self._sell_cancel_on_the_fly_orders),
        len(self._buy_orders),
        len(self._buy_on_the_fly_orders),
        len(self._buy_working_orders),
        len(self._buy_cancel_on_the_fly_orders),
    ))


class WorkingOrderStateGenerator(WorkingOrderState):
  def __init__(self, product, og, sell_order_side, buy_order_side):
    super().__init__(product)
    assert isinstance(og, OrderGatewayBase), type(og)
    assert isinstance(sell_order_side, (list, tuple)), sell_order_side
    assert isinstance(buy_order_side, (list, tuple)), buy_order_side
    self._og = og
    self._SellOrderSide = tuple(sell_order_side)
    self._BuyOrderSide = tuple(buy_order_side)

  def _update_order_state(self):
    self._reset_order_state()

    for order in self._og.get_working_order():
      if order.product != self.product:
        continue

      if order.order_side in self._SellOrderSide:
        if order.cancel_sent:
          self._sell_cancel_on_the_fly_orders.append(order)
          self._sell_cancel_on_the_fly_potential -= order.qty
        elif order.accepted:
          self._sell_working_orders.append(order)
          self._sell_working_potential -= order.qty
        else:
          self._sell_on_the_fly_orders.append(order)
          self._sell_on_the_fly_potential -= order.qty
        self._sell_orders.append(order)
        self._sell_potential -= order.qty

      elif order.order_side in self._BuyOrderSide:
        if order.cancel_sent:
          self._buy_cancel_on_the_fly_orders.append(order)
          self._buy_cancel_on_the_fly_potential += order.qty
        elif order.accepted:
          self._buy_working_orders.append(order)
          self._buy_working_potential += order.qty
        else:
          self._buy_on_the_fly_orders.append(order)
          self._buy_on_the_fly_potential += order.qty
        self._buy_orders.append(order)
        self._buy_potential += order.qty

      else:
        raise ValueError('Unknown side: %s', str(order))

    self._sell_orders = sorted(self._sell_orders, key=lambda order: (order.price, order.order_id))
    self._buy_orders = sorted(self._buy_orders, key=lambda order: (-order.price, order.order_id))
